package cn.ingenic.glasssync.contact;

import java.util.List;

import android.content.ContentProvider;
import android.content.ContentResolver;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.net.Uri;
import android.util.Log;
import cn.ingenic.glasssync.db.ContactsDBHelper;
import cn.ingenic.glasssync.db.DBContacts;
import cn.ingenic.glasssync.db.DBContactsData;
import cn.ingenic.glasssync.entity.ContactsDataDto;
import cn.ingenic.glasssync.entity.ContactsDto;
import cn.ingenic.glasssync.contact.HanziToPinyin;
import cn.ingenic.glasssync.contact.HanziToPinyin.Token;

public class ContactsProvider extends ContentProvider {
        private static final String TAG = "ContactsProvider";
        private static final boolean DEBUG = false;

        private static final UriMatcher mUriMatcher;
        private static final String AUTHORITY = "cn.ingenic.glasssync.contacts";
        private static final String AUTHORITY_TAB_CONTACTS = "/contacts";
        private static final String AUTHORITY_TAB_DATA = "/data";
        private static final String AUTHORITY_VIEW_VDATA = "/vdata";
        private static final String AUTHORITY_DQL_PHONETONAME = "/phonetoname";
        private static final String AUTHORITY_CLEARALL = "/clearalldata";
        public static final Uri URI_CONTACTS = Uri.parse("content://" + AUTHORITY + AUTHORITY_TAB_CONTACTS);
        public static final Uri URI_DATA = Uri.parse("content://" + AUTHORITY + AUTHORITY_TAB_DATA);
        public static final Uri URI_VDATA = Uri.parse("content://" + AUTHORITY + AUTHORITY_VIEW_VDATA);
        public static final Uri URI_PHONETONAME = Uri.parse("content://" + AUTHORITY + AUTHORITY_DQL_PHONETONAME);
        public static final Uri URI_CLEARALL = Uri.parse("content://" + AUTHORITY + AUTHORITY_CLEARALL);

        private static final int TYPE_TABLE_CONTACTS = 0;
        private static final int TYPE_TABLE_DATA = 1;
        private static final int TYPE_VIEW_VDATA = 2;
        private static final int TYPE_PHONETONAME = 3;
        private static final int TYPE_CLEAR_ALL = 4;

        public static final String KEY_CONTACTS_ID = "c_id";
        public static final String KEY_CONTACTS_DISPLAYNAME = "c_display_name";

        public static final String KEY_DATA_ID = "d_id";
        public static final String KEY_DATA_CONTACTID = "d_contact_id";
        public static final String KEY_DATA_MIMETYPE = "d_mimetype";
        public static final String KEY_DATA_TYPE = "d_type";
        public static final String KEY_DATA_DATA = "d_data";

        private ContentResolver mResolver = null;
        private Context mContext = null;
        private ContactsDBHelper mContentDBHelper = null;
        private SQLiteDatabase mDB = null;
        private DBContacts mDBContacts = null;
        private DBContactsData mDBData = null;

        static {
                mUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);
                mUriMatcher.addURI(AUTHORITY, AUTHORITY_TAB_CONTACTS, TYPE_TABLE_CONTACTS);
                mUriMatcher.addURI(AUTHORITY, AUTHORITY_TAB_DATA, TYPE_TABLE_DATA);
                mUriMatcher.addURI(AUTHORITY, AUTHORITY_VIEW_VDATA, TYPE_VIEW_VDATA);
                mUriMatcher.addURI(AUTHORITY, AUTHORITY_DQL_PHONETONAME, TYPE_PHONETONAME);
                mUriMatcher.addURI(AUTHORITY, AUTHORITY_CLEARALL, TYPE_CLEAR_ALL);
        }

        @Override
        public boolean onCreate() {
                mContext = getContext();
                mContentDBHelper = ContactsDBHelper.getInstance(mContext, null);
                mResolver = mContext.getContentResolver();
                mDB = mContentDBHelper.getWritableDatabase();
                mDBContacts = new DBContacts(mDB);
                mDBData = new DBContactsData(mDB);
                return false;
        }

        @Override
        public String getType(Uri uri) {
                return null;
        }

        @Override
        public Cursor query(Uri uri, String[] projection, String selection,
                        String[] selectionArgs, String sortOrder) {
                if (DEBUG) Log.d(TAG, "query uri=" + uri);
                Cursor cursor = null;
                switch (mUriMatcher.match(uri)) {
                case TYPE_TABLE_CONTACTS:
                        cursor = mDBContacts.customQuery(projection, selection, selectionArgs, null, null, sortOrder);
                        break;
                case TYPE_TABLE_DATA:
                        cursor = mDBData.customQuery(projection, selection, selectionArgs, null, null, sortOrder);
                        break;
                case TYPE_VIEW_VDATA:
                case TYPE_PHONETONAME:
                        cursor = mDBContacts.customQueryVdata(projection, selection, selectionArgs, null, null, sortOrder);
                        break;
                default:
                        Log.w(TAG, "Cannot query by URI: " + uri);
                }
                return cursor;
        }

        @Override
        public Uri insert(Uri uri, ContentValues values) {
                if (DEBUG) Log.d(TAG, "insert URI = " + uri);
                long id = -1;

                switch (mUriMatcher.match(uri)) {
                case TYPE_TABLE_CONTACTS: {
                        ContactsDto contactsDto = new ContactsDto();
                        String name = values.getAsString(KEY_CONTACTS_DISPLAYNAME);
                        String pinYin = getPinYin(name);
                        int sortKey = genWatchSortKey(pinYin);
                        contactsDto.setDisplayName(name);
                        contactsDto.setWatchSortKey(sortKey);
                        id = mDBContacts.insert(contactsDto);
                        break;
                }
                case TYPE_TABLE_DATA: {
                        ContactsDataDto dataDto = new ContactsDataDto();
                        dataDto.setContactId(values.getAsInteger(KEY_DATA_CONTACTID));
                        dataDto.setMimeType(values.getAsString(KEY_DATA_MIMETYPE));
                        dataDto.setType(values.getAsString(KEY_DATA_TYPE));
                        dataDto.setData(values.getAsString(KEY_DATA_DATA));
                        id = mDBData.insert(dataDto);
                        break;
                }
                default: {
                        Log.w(TAG, "Cannot insert by URI: " + uri);
                }
                }

                if (id >= 0) {
                        return ContentUris.withAppendedId(uri, id);
                }
                return null;
        }

        @Override
        public int update(Uri uri, ContentValues values, String selection,
                        String[] selectionArgs) {
                if (DEBUG) Log.d(TAG, "update uri=" + uri);
                int result = -1;

                switch (mUriMatcher.match(uri)) {
                case TYPE_TABLE_CONTACTS: {
                        ContactsDto contactsDto = new ContactsDto();
                        String name = values.getAsString(KEY_CONTACTS_DISPLAYNAME);
                        String pinYin = getPinYin(name);
                        int sortKey = genWatchSortKey(pinYin);
                        contactsDto.setDisplayName(name);
                        contactsDto.setWatchSortKey(sortKey);
                        result = mDBContacts.updateByConditions(contactsDto, selection, selectionArgs);
                        if (result > 0) {
                                mResolver.notifyChange(URI_CONTACTS, null);
                        }
                        break;
                }
                case TYPE_TABLE_DATA: {
                        ContactsDataDto dataDto = new ContactsDataDto();
                        dataDto.setContactId(values.getAsInteger(KEY_DATA_CONTACTID));
                        dataDto.setMimeType(values.getAsString(KEY_DATA_MIMETYPE));
                        dataDto.setType(values.getAsString(KEY_DATA_TYPE));
                        dataDto.setData(values.getAsString(KEY_DATA_DATA));
                        result = mDBData.updateByConditions(dataDto, selection, selectionArgs);
                        if (result > 0) {
                                mResolver.notifyChange(URI_DATA, null);
                        }
                        break;
                }
                default:
                        Log.w(TAG, "Cannot update by URI: " + uri);
                }

                return result;
        }

        @Override
        public int delete(Uri uri, String selection, String[] selectionArgs) {
                if (DEBUG) Log.d(TAG, "delete URI = " + uri);
                int result = -1;

                switch (mUriMatcher.match(uri)) {
                case TYPE_TABLE_CONTACTS:
                        result = mDBContacts.deleteByConditions(selection, selectionArgs);
                        break;
                case TYPE_TABLE_DATA:
                        result = mDBData.deleteByConditions(selection, selectionArgs);
                        break;
                case TYPE_CLEAR_ALL:
                        mContentDBHelper.clearAllData();
                        mResolver.notifyChange(URI_CONTACTS, null);
                        mResolver.notifyChange(URI_DATA, null);
                        break;
                default:
                        Log.w(TAG, "Cannot delete by URI: " + uri);
                }

                return result;
        }

        /**
         * Convert Chinese character to PinYin
         * @param hanzi Chinese character
         * @return The PinYin is upper case.
         */
        private String getPinYin (String hanzi) {
                List<Token> tokens = HanziToPinyin.getInstance().get(hanzi);
                StringBuilder builder = new StringBuilder("");
                if ((null != tokens) && (tokens.size() > 0)) {
                        for (Token token : tokens) {
                                if (Token.PINYIN == token.type) {
                                        builder.append(token.target);
                                } else {
                                        builder.append(token.source);
                                }
                        }
                }
                return builder.toString().toUpperCase();
        }

        private int genWatchSortKey (String pinYin) {
                int result = 0;

                if ((null == pinYin) || ("".equals(pinYin))) {
                        return result;
                }

                result = pinYin.charAt(0);
                result = result < 'A' ? 'A' : result;
                result = result > 'Z' ? 'Z' : result;
                result -= 'A';

                return result;
        }

}
